package sketch.specs.annotation;

import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.StringLiteral;

import sketch.ast.ASTUtils;
import sketch.util.Checker;

public final class AnnotationMethodProcessor {
	
	public static String source = "source";
	public static String dest = "dest";
	public static String repls = "repls";

	public final MethodDeclaration method_declare;
	
	private List<Map<String, String>> mapping_list = null;
	
	public AnnotationMethodProcessor(MethodDeclaration method) {
		Checker.checkNull(method, "The method declaration could not be null.");
		this.method_declare = method;
	}
	
	public List<Map<String, String>> getMappings() {
		if(mapping_list != null) {
			return mapping_list;
		} else {
			mapping_list = this.process_to_get_mapping_list();
			return mapping_list;
		}
	}
	
	private List<Map<String, String>> process_to_get_mapping_list() {
		mapping_list = new LinkedList<Map<String, String>>();
		NormalAnnotation repls_annotation = ASTUtils.getMethodAnnotation(method_declare, Repls.class);
		NormalAnnotation repl_annotation = ASTUtils.getMethodAnnotation(method_declare, Repl.class);
		if(repls_annotation == null && repl_annotation == null) {
			return mapping_list;
		}
		Checker.checkNull(repl_annotation == null || repls_annotation == null,  "One of these should be null.");
		if(repls_annotation != null) {
			List<MemberValuePair> valuePairs = repls_annotation.values();
			for(MemberValuePair valuePair : valuePairs) {
				//System.out.println(valuePair);
				Checker.checkTrue(valuePair.getName().toString().equals(repls), "the name should be repls");
				ArrayInitializer initializer = (ArrayInitializer)valuePair.getValue();
				List<Expression> expression_list = initializer.expressions();
				for(Expression e : expression_list) {
					NormalAnnotation annotation = (NormalAnnotation)e;
					Map<String, String> map = this.extract_value_pair(annotation);
					mapping_list.add(map);
				}
			}
		} else if (repl_annotation != null) {
			Map<String, String> map = this.extract_value_pair(repl_annotation);
			mapping_list.add(map);
			
		}
		//System.out.println("   " + mapping_list);
		return mapping_list;
	}
	
	private Map<String, String> extract_value_pair(NormalAnnotation repl_annotation) {
		Map<String, String> map = new LinkedHashMap<String, String>();
		
		List<MemberValuePair> valuePairs = repl_annotation.values();
		Checker.checkTrue(valuePairs.size() == 2, "The size of value pair in Repl annotation should be exactly 2.");
		MemberValuePair p0 = valuePairs.get(0);
		MemberValuePair p1 = valuePairs.get(1);
		if(p0.getName().toString().equals(source)) {
			Checker.checkTrue(p1.getName().toString().equals(dest), "The annotation " + repl_annotation + " is illegal, no dest pair.");
		} else {
			Checker.checkTrue(p0.getName().toString().equals(dest) && p1.getName().toString().equals(source),
					"The annotation: " + repl_annotation + " is illegal.");
			MemberValuePair tmp = p0;
			p0 = p1;
			p1 = tmp;
		}
		if(p0.getValue() instanceof StringLiteral) {
			throw new RuntimeException("Please use the full format: Repl(source={o1, o2}, dest={n1, n2})");
		}
		ArrayInitializer initializer_source = (ArrayInitializer)p0.getValue();
		ArrayInitializer initializer_dest = (ArrayInitializer)p1.getValue();
		Checker.checkTrue(initializer_source.expressions().size() == initializer_dest.expressions().size(),
				"The annotation is not legal: " + repl_annotation);
		
		//Map<String, String> map = new LinkedHashMap<String, String>();
		for(int i = 0; i < initializer_source.expressions().size(); i++) {
			String str = ((StringLiteral)(initializer_source.expressions().get(i))).getLiteralValue();
			String dest = ((StringLiteral)(initializer_dest.expressions().get(i))).getLiteralValue();
			map.put(str, dest);
		}
		//mapping_list.add(map);
		
		return map;
	}
	
	
	/**
	 * A sample usage
	 * */
	public static void main(String[] args) {
		String file_path = "./tests/sketch/specs/annotation/ClassUseRepTest.java";
		List<MethodDeclaration> methods = ASTUtils.getAllMethods(ASTUtils.astRoot(file_path));
		for(MethodDeclaration method : methods) {
			AnnotationMethodProcessor processor = new AnnotationMethodProcessor(method);
			processor.getMappings();
		}
	}
}
